package com.tools.web.interceptor.auth;

import com.fasterxml.jackson.core.type.TypeReference;
import com.tools.common.container.OwnPairs;
import com.tools.common.exception.ParseRuntimeException;
import com.tools.common.object.Note;
import com.tools.common.object.json.JsonKit;
import com.tools.common.object.string.StrKit;
import com.tools.common.thread.TimePair;
import com.tools.web.http.HttpDataFromEnum;
import com.tools.web.interceptor.access.AccessSource;
import com.tools.web.work.ResultCode;

import java.util.*;

/**
 * [身份认证拦截器] 的配置项包装类
 * */
@Note("[身份认证拦截器] 的配置项包装类")
public class AuthSource {

    @Note("AuthToken 位于 HTTP 请求的哪个地方")
    private HttpDataFromEnum tokenIn;

    @Note("AuthToken 的名称")
    private String tokenName;

    @Note("缓存 AuthUser 对象时的 key 的前缀")
    private String authUserSaveKeyPrefix;

    @Note("设备类型集合")
    private final Set<String> loginDeviceTypes;

    @Note("多端在线登录时才用得上：【登录设备类型-最大可在线数】的映射集合")
    private Map<String, Integer> loginDeviceTypeAndMaxOnlineTotal;

    @Note("平台禁用时，平台代表的唯一 ID")
    private Object disabledPlatformId;

    @Note("【异常 JSON 信息】: 没有获取到 AuthToken")
    private String tokenNotFoundErrorJson;

    @Note("【异常 JSON 信息】: AuthToken 解析失败或者已过期")
    private String tokenParseFailedErrorJson;

    @Note("【异常 JSON 信息】: AuthToken 已过期")
    private String tokenExpiredErrorJson;

    @Note("【续期功能开启时】：当 AuthToken 需要进行续期时的所需的工具实例")
    private AuthTokenExtender authTokenExtender;

    @Note("【在认证拦截器里集成授权拦截器时】：授权拦截器的具体实现类的全路径类名")
    private String accessManagerClassName;

    @Note("【在认证拦截器里集成授权拦截器时】：授权拦截器的配置项包装类实例")
    private AccessSource accessSource;

    /* **************************************************************************************
     *
     *          构造器
     *
     * **************************************************************************************
     * */

    public AuthSource() {
        this.tokenIn = HttpDataFromEnum.HEADER;
        this.tokenName = "Authorization";
        this.authUserSaveKeyPrefix = "";
        this.disabledPlatformId = -1;
        this.loginDeviceTypes = new HashSet<>(5);
        this.loginDeviceTypes.add(LoginDeviceType.BROWSER.getValue());
        this.loginDeviceTypes.add(LoginDeviceType.APP.getValue());
        this.loginDeviceTypes.add(LoginDeviceType.MINIAPP.getValue());
    }

    public static AuthSource of() {
        return new AuthSource();
    }

    /* **************************************************************************************
     *
     *          Getter/Setter
     *
     * **************************************************************************************
     * */

    public HttpDataFromEnum getTokenIn() {
        return tokenIn;
    }

    public void setTokenIn(HttpDataFromEnum tokenIn) {
        if(tokenIn == null) throw new NullPointerException("AuthToken 的来源为 null");
        this.tokenIn = tokenIn;
    }

    public void setTokenIn(String tokenIn) {
        this.tokenIn = HttpDataFromEnum.getEnum(tokenIn);
    }

    public String getTokenName() {
        return tokenName;
    }

    public void setTokenName(String tokenName) {
        if(tokenName == null || tokenName.isEmpty()) throw new NullPointerException("AuthToken 的 get name 为空");
        this.tokenName = tokenName;
    }

    public String getAuthUserSaveKeyPrefix() {
        return authUserSaveKeyPrefix;
    }

    public void setAuthUserSaveKeyPrefix(String authUserSaveKeyPrefix) {
        this.authUserSaveKeyPrefix = (authUserSaveKeyPrefix != null) ? authUserSaveKeyPrefix : "";
    }

    public Set<String> getLoginDeviceTypes() {
        return loginDeviceTypes;
    }

    public void setLoginDeviceTypes(String... loginDeviceTypes) {
        if(loginDeviceTypes != null && loginDeviceTypes.length > 0) {
            for(String loginDeviceType : loginDeviceTypes) {
                if(loginDeviceType == null || loginDeviceType.isEmpty()) throw new NullPointerException("不能设置空的设备类型");
                this.loginDeviceTypes.add(loginDeviceType);
            }
        }
    }

    public void setLoginDeviceTypes(Iterable<String> loginDeviceTypes) {
        for(String loginDeviceType : loginDeviceTypes) {
            if(loginDeviceType == null || loginDeviceType.isEmpty()) throw new NullPointerException("不能设置空的设备类型");
            this.loginDeviceTypes.add(loginDeviceType);
        }
    }

    public Map<String, Integer> getLoginDeviceTypeAndMaxOnlineTotal() {
        return loginDeviceTypeAndMaxOnlineTotal;
    }

    public void setLoginDeviceTypeAndMaxOnlineTotal(String loginDeviceTypeAndMaxOnlineTotalJson) {
        if(loginDeviceTypeAndMaxOnlineTotalJson == null) return;
        try {
            this.loginDeviceTypeAndMaxOnlineTotal =
                    JsonKit.toEntity(loginDeviceTypeAndMaxOnlineTotalJson,
                            new TypeReference<HashMap<String, Integer>>() {});
        } catch (Exception e){
            throw new ParseRuntimeException(e, loginDeviceTypeAndMaxOnlineTotalJson +
                    " 转为 java.util.HashMap<String, Integer> 类型失败");
        }
    }

    public void setLoginDeviceTypeAndMaxOnlineTotal(Map<String, Integer> loginDeviceTypeAndMaxOnlineTotal) {
        this.loginDeviceTypeAndMaxOnlineTotal = loginDeviceTypeAndMaxOnlineTotal;
    }

    public void setLoginDeviceTypeAndMaxOnlineTotal(OwnPairs<String, Integer> loginDeviceTypeAndMaxOnlineTotal) {
        if(loginDeviceTypeAndMaxOnlineTotal != null && !loginDeviceTypeAndMaxOnlineTotal.isEmpty()) {
            this.loginDeviceTypeAndMaxOnlineTotal = loginDeviceTypeAndMaxOnlineTotal.toMap();
        } else{
            this.loginDeviceTypeAndMaxOnlineTotal = null;
        }
    }

    public Object getDisabledPlatformId() {
        return disabledPlatformId;
    }

    public void setDisabledPlatformId(Object disabledPlatformId) {
        if(disabledPlatformId != null) {
            this.disabledPlatformId = disabledPlatformId;
        }
    }

    public String getTokenNotFoundErrorJson() {
        if(this.tokenNotFoundErrorJson == null || this.tokenNotFoundErrorJson.isEmpty()) {
            return ResultCode.NOT_FOUND_TOKEN.toJSON();
        }
        return tokenNotFoundErrorJson;
    }

    public void setTokenNotFoundErrorJson(String tokenNotFoundErrorJson) {
        if(tokenNotFoundErrorJson == null || tokenNotFoundErrorJson.isEmpty()) {
            throw new NullPointerException("[AuthToken 没有获取到的错误响应信息] 为空");
        }
        this.tokenNotFoundErrorJson = tokenNotFoundErrorJson;
    }

    public String getTokenParseFailedErrorJson() {
        if(this.tokenParseFailedErrorJson == null || this.tokenParseFailedErrorJson.isEmpty()) {
            return ResultCode.AUTH_PARSE_FAIL.toJSON();
        }
        return tokenParseFailedErrorJson;
    }

    public void setTokenParseFailedErrorJson(String tokenParseFailedErrorJson) {
        if(tokenParseFailedErrorJson == null || tokenParseFailedErrorJson.isEmpty()) {
            throw new NullPointerException("[AuthToken 解析失败的错误响应信息] 为空");
        }
        this.tokenParseFailedErrorJson = tokenParseFailedErrorJson;
    }

    public String getTokenExpiredErrorJson() {
        if(this.tokenExpiredErrorJson == null || this.tokenExpiredErrorJson.isEmpty()) {
            return ResultCode.AUTH_EXPIRED.toJSON();
        }
        return tokenExpiredErrorJson;
    }

    public void setTokenExpiredErrorJson(String tokenExpiredErrorJson) {
        if(tokenExpiredErrorJson == null || tokenExpiredErrorJson.isEmpty()) {
            throw new NullPointerException("[AuthToken 已过期的错误响应信息] 为空");
        }
        this.tokenExpiredErrorJson = tokenExpiredErrorJson;
    }

    public AuthTokenExtender getAuthTokenExtender() {
        return authTokenExtender;
    }

    public void setAuthTokenExtender(AuthTokenExtender authTokenExtender) {
        this.authTokenExtender = authTokenExtender;
    }

    public String getAccessManagerClassName() {
        return accessManagerClassName;
    }

    public void setAccessManagerClassName(String accessManagerClassName) {
        this.accessManagerClassName = accessManagerClassName;
    }

    public AccessSource getAccessSource() {
        return accessSource;
    }

    public void setAccessSource(AccessSource accessSource) {
        this.accessSource = accessSource;
    }

    @Override
    public String toString() {
        return "AuthSource{" +
                "tokenIn=" + tokenIn +
                ", tokenName=" + tokenName +
                ", authUserSaveKeyPrefix=" + authUserSaveKeyPrefix +
                ", loginDeviceTypes" + loginDeviceTypes +
                ", loginDeviceTypeAndMaxOnlineTotal=" + loginDeviceTypeAndMaxOnlineTotal +
                ", disabledPlatformId=" + disabledPlatformId +
                ", tokenNotFoundErrorJson=" + tokenNotFoundErrorJson +
                ", tokenParseFailedErrorJson=" + tokenParseFailedErrorJson +
                ", tokenExpiredErrorJson=" + tokenExpiredErrorJson +
                ", authTokenExtender=" + authTokenExtender +
                ", accessManagerClassName=" + accessManagerClassName +
                ", accessSource=" + accessSource +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        AuthSource that = (AuthSource) o;
        return tokenIn == that.tokenIn && Objects.equals(tokenName, that.tokenName) && Objects.equals(authUserSaveKeyPrefix, that.authUserSaveKeyPrefix) && Objects.equals(loginDeviceTypes, that.loginDeviceTypes) && Objects.equals(loginDeviceTypeAndMaxOnlineTotal, that.loginDeviceTypeAndMaxOnlineTotal) && Objects.equals(disabledPlatformId, that.disabledPlatformId) && Objects.equals(tokenNotFoundErrorJson, that.tokenNotFoundErrorJson) && Objects.equals(tokenParseFailedErrorJson, that.tokenParseFailedErrorJson) && Objects.equals(tokenExpiredErrorJson, that.tokenExpiredErrorJson) && Objects.equals(authTokenExtender, that.authTokenExtender) && Objects.equals(accessManagerClassName, that.accessManagerClassName) && Objects.equals(accessSource, that.accessSource);
    }

    @Override
    public int hashCode() {
        return Objects.hash(tokenIn, tokenName, authUserSaveKeyPrefix, loginDeviceTypes, loginDeviceTypeAndMaxOnlineTotal, disabledPlatformId, tokenNotFoundErrorJson, tokenParseFailedErrorJson, tokenExpiredErrorJson, authTokenExtender, accessManagerClassName, accessSource);
    }

    /* **************************************************************************************
     *
     *          链式调用
     *
     * **************************************************************************************
     * */

    public AuthSource tokenIn(HttpDataFromEnum tokenIn) {
        this.setTokenIn(tokenIn);
        return this;
    }

    public AuthSource tokenIn(String tokenIn) {
        this.setTokenIn(tokenIn);
        return this;
    }

    public AuthSource tokenName(String tokenName) {
        this.setTokenName(tokenName);
        return this;
    }

    public AuthSource authUserSaveKeyPrefix(String authUserSaveKeyPrefix) {
        this.setAuthUserSaveKeyPrefix(authUserSaveKeyPrefix);
        return this;
    }

    public AuthSource loginDeviceTypes(String... loginDeviceTypes) {
        this.setLoginDeviceTypes(loginDeviceTypes);
        return this;
    }

    public AuthSource loginDeviceTypes(Iterable<String> loginDeviceTypes) {
        this.setLoginDeviceTypes(loginDeviceTypes);
        return this;
    }

    public AuthSource loginDeviceTypeAndMaxOnlineTotal(String loginDeviceTypeAndMaxOnlineTotalJson) {
        this.setLoginDeviceTypeAndMaxOnlineTotal(loginDeviceTypeAndMaxOnlineTotalJson);
        return this;
    }

    public AuthSource loginDeviceTypeAndMaxOnlineTotal(Map<String, Integer> loginDeviceTypeAndMaxOnlineTotal) {
        this.setLoginDeviceTypeAndMaxOnlineTotal(loginDeviceTypeAndMaxOnlineTotal);
        return this;
    }

    public AuthSource loginDeviceTypeAndMaxOnlineTotal(OwnPairs<String, Integer> loginDeviceTypeAndMaxOnlineTotal) {
        this.setLoginDeviceTypeAndMaxOnlineTotal(loginDeviceTypeAndMaxOnlineTotal);
        return this;
    }

    public AuthSource disabledPlatformId(Objects disabledPlatformId) {
        this.setDisabledPlatformId(disabledPlatformId);
        return this;
    }

    public AuthSource tokenNotFoundErrorJson(String tokenNotFoundErrorJson) {
        this.setTokenNotFoundErrorJson(tokenNotFoundErrorJson);
        return this;
    }

    public AuthSource tokenParseFailedErrorJson(String tokenParseFailedErrorJson) {
        this.setTokenParseFailedErrorJson(tokenParseFailedErrorJson);
        return this;
    }

    public AuthSource tokenExpiredErrorJson(String tokenExpiredErrorJson) {
        this.setTokenExpiredErrorJson(tokenExpiredErrorJson);
        return this;
    }

    public AuthSource authTokenExtender(AuthTokenExtender authTokenExtender) {
        this.setAuthTokenExtender(authTokenExtender);
        return this;
    }

    public AuthSource accessManagerClassName(String accessManagerClassName) {
        this.setAccessManagerClassName(accessManagerClassName);
        return this;
    }

    public AuthSource accessSource(AccessSource accessSource) {
        this.setAccessSource(accessSource);
        return this;
    }


    @Note("读取 properties 里的配置项，转为实例返回")
    public static AuthSource from(Map properties) {
        AuthSource source = new AuthSource();
        if(properties == null || properties.isEmpty()) return source;
        source.setTokenIn((String) properties.get("auth-manager-tokenIn"));
        source.setTokenName((String) properties.get("auth-manager-tokenName"));
        source.setAuthUserSaveKeyPrefix((String) properties.get("auth-manager-authUserSaveKeyPrefix"));
        String loginDeviceTypes = (String) properties.get("auth-manager-loginDeviceTypes");
        source.setLoginDeviceTypes(StrKit.split(loginDeviceTypes, ","));
        source.setLoginDeviceTypeAndMaxOnlineTotal((String) properties.get("auth-manager-loginDeviceTypeAndMaxOnlineTotal"));
        source.setDisabledPlatformId(properties.get("auth-manager-disabledPlatformId"));
        source.setTokenNotFoundErrorJson((String) properties.get("auth-manager-tokenNotFoundErrorJson"));
        source.setTokenParseFailedErrorJson((String) properties.get("auth-manager-tokenParseFailedErrorJson"));
        source.setTokenExpiredErrorJson((String) properties.get("auth-manager-tokenExpiredErrorJson"));
        //token 续期
        AuthTokenExtender authTokenExtender = private_newAuthTokenExtenderByProperties(properties);
        if(authTokenExtender != null) {
            source.setAuthTokenExtender(authTokenExtender);
        }
        //在认证管理器里开启授权功能
        String authorizeTypeClassName = (String) properties.get("auth-manager-enable-authorizeType");
        if(authorizeTypeClassName != null && !authorizeTypeClassName.isEmpty()) {
            source.setAccessManagerClassName(authorizeTypeClassName);
            AccessSource accessSource = AccessSource.from(properties);
            source.setAccessSource(accessSource);
        }
        return source;
    }



    @Note("根据配置项构造 AuthTokenExtender 续期工具实例")
    private static AuthTokenExtender private_newAuthTokenExtenderByProperties(Map properties) {
        String extendMethod = (String) properties.get("auth-manager-tokenExtend-method");
        if(extendMethod == null || extendMethod.isEmpty()) return null;
        //续期时间
        String extendTimeText = (String) properties.get("auth-manager-tokenExtend-time");
        TimePair extendTime = TimePair.buildSafe(extendTimeText);
        if(extendTime == null) return null;
        //最大允许续期时间
        String maxAllowExtendTimeText = (String) properties.get("auth-manager-tokenExtend-maxAllowExtendTime");
        TimePair maxAllowExtendTime = TimePair.buildSafe(maxAllowExtendTimeText);
        if(maxAllowExtendTime == null) return null;
        //最大允许续期次数
        Integer maxAllowExtendCount = (Integer) properties.get("auth-manager-tokenExtend-maxAllowExtendCount");
        if(maxAllowExtendCount == null) {
            return null;
        }
        switch (extendMethod) {
            case "simple": return new SimpleAuthTokenExtender(extendTime, maxAllowExtendTime, maxAllowExtendCount);
            case "refresh": {
                String refreshTokenInText = (String) properties.get("auth-manager-tokenExtend-refreshTokenIn");
                HttpDataFromEnum refreshTokenIn = HttpDataFromEnum.getEnum(refreshTokenInText);
                String refreshTokenName = (String) properties.get("auth-manager-tokenExtend-refreshTokenName");
                return new RefreshAuthTokenExtender(refreshTokenName, refreshTokenIn, extendTime, maxAllowExtendTime, maxAllowExtendCount);
            }
            default: throw new IllegalArgumentException("暂不支持 " + extendMethod + " 的 AuthToken 续期方式");
        }
    }

}
